package za.co.immedia.pinnedheaderlistview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;

public class PinnedHeaderListView extends ListView implements OnScrollListener
{

	private OnScrollListener mOnScrollListener;

	private PinnedSectionedHeaderAdapter mAdapter;
	private View mCurrentHeader;
	private int mCurrentHeaderViewType = 0;
	private float mHeaderOffset;
	private boolean mShouldPin = true;
	private int mCurrentSection = 0;
	private int mWidthMode;

	@SuppressWarnings("unused")
	private int mHeightMode;

	public PinnedHeaderListView(Context context)
	{
		super(context);
		super.setOnScrollListener(this);
	}

	public PinnedHeaderListView(Context context, AttributeSet attrs)
	{
		super(context, attrs);
		super.setOnScrollListener(this);
	}

	public PinnedHeaderListView(Context context, AttributeSet attrs,
			int defStyle)
	{
		super(context, attrs, defStyle);
		super.setOnScrollListener(this);
	}

	public void setPinHeaders(boolean shouldPin)
	{
		mShouldPin = shouldPin;
	}

	@Override
	public void setAdapter(ListAdapter adapter)
	{
		mCurrentHeader = null;
		mAdapter = (PinnedSectionedHeaderAdapter) adapter;
		super.setAdapter(adapter);
	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount)
	{
		if (mOnScrollListener != null)
		{
			mOnScrollListener.onScroll(view, firstVisibleItem,
					visibleItemCount, totalItemCount);
		}

		if (mAdapter == null || mAdapter.getCount() == 0 || !mShouldPin
				|| (firstVisibleItem < getHeaderViewsCount()))
		{
			mCurrentHeader = null;
			mHeaderOffset = 0.0f;
			for (int i = firstVisibleItem; i < firstVisibleItem
					+ visibleItemCount; i++)
			{
				View header = getChildAt(i);
				if (header != null)
				{
					header.setVisibility(VISIBLE);
				}
			}
			return;
		}

		firstVisibleItem -= getHeaderViewsCount();

		int section = mAdapter.getSectionForPosition(firstVisibleItem);
		int viewType = mAdapter.getSectionHeaderViewType(section);
		mCurrentHeader = getSectionHeaderView(section,
				mCurrentHeaderViewType != viewType ? null : mCurrentHeader);
		ensurePinnedHeaderLayout(mCurrentHeader);
		mCurrentHeaderViewType = viewType;

		mHeaderOffset = 0.0f;

		for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++)
		{
			if (mAdapter.isSectionHeader(i))
			{
				View header = getChildAt(i - firstVisibleItem);
				float headerTop = header.getTop();
				float pinnedHeaderHeight = mCurrentHeader.getMeasuredHeight();
				header.setVisibility(VISIBLE);
				if (pinnedHeaderHeight >= headerTop && headerTop > 0)
				{
					mHeaderOffset = headerTop - header.getHeight();
				}
				else if (headerTop <= 0)
				{
					header.setVisibility(INVISIBLE);
				}
			}
		}

		invalidate();
	}

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState)
	{
		if (mOnScrollListener != null)
		{
			mOnScrollListener.onScrollStateChanged(view, scrollState);
		}
	}

	private View getSectionHeaderView(int section, View oldView)
	{
		boolean shouldLayout = section != mCurrentSection || oldView == null;

		View view = mAdapter.getSectionHeaderView(section, oldView, this);
		if (shouldLayout)
		{
			// a new section, thus a new header. We should lay it out again
			ensurePinnedHeaderLayout(view);
			mCurrentSection = section;
		}
		return view;
	}

	private void ensurePinnedHeaderLayout(View header)
	{
		if (header.isLayoutRequested())
		{
			int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
					mWidthMode);

			int heightSpec;
			ViewGroup.LayoutParams layoutParams = header.getLayoutParams();
			if (layoutParams != null && layoutParams.height > 0)
			{
				heightSpec = MeasureSpec.makeMeasureSpec(layoutParams.height,
						MeasureSpec.EXACTLY);
			}
			else
			{
				heightSpec = MeasureSpec.makeMeasureSpec(0,
						MeasureSpec.UNSPECIFIED);
			}
			header.measure(widthSpec, heightSpec);
			header.layout(0, 0, header.getMeasuredWidth(),
					header.getMeasuredHeight());
		}
	}

	@Override
	protected void dispatchDraw(Canvas canvas)
	{
		super.dispatchDraw(canvas);
		if (mAdapter == null || !mShouldPin || mCurrentHeader == null)
			return;
		int h = getDividerHeight() + mCurrentHeader.getMeasuredHeight();
		int saveCount = canvas.save();
		canvas.translate(0, mHeaderOffset);
		canvas.clipRect(0, 0, getWidth(), h); // needed
		// for
		// <
		// HONEYCOMB
		mCurrentHeader.draw(canvas);

		Drawable drawable = getDivider();
		if (getDividerHeight() > 0 && drawable != null)
		{
			drawable.setBounds(0, h - getDividerHeight(), getWidth(), h);
			drawable.draw(canvas);
		}

		canvas.restoreToCount(saveCount);
	}

	@Override
	public void setOnScrollListener(OnScrollListener l)
	{
		mOnScrollListener = l;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

		mWidthMode = MeasureSpec.getMode(widthMeasureSpec);
		mHeightMode = MeasureSpec.getMode(heightMeasureSpec);
	}

	public void setOnItemClickListener(
			PinnedHeaderListView.OnItemClickListener listener)
	{
		super.setOnItemClickListener(listener);
	}

	public static abstract class OnItemClickListener implements
			AdapterView.OnItemClickListener
	{
		@Override
		public void onItemClick(AdapterView<?> adapterView, View view,
				int rawPosition, long id)
		{
			int headCount = ((PinnedHeaderListView) adapterView)
					.getHeaderViewsCount();
			SectionedBaseAdapter adapter;
			if (adapterView.getAdapter().getClass()
					.equals(HeaderViewListAdapter.class))
			{
				HeaderViewListAdapter wrapperAdapter = (HeaderViewListAdapter) adapterView
						.getAdapter();
				adapter = (SectionedBaseAdapter) wrapperAdapter
						.getWrappedAdapter();
			}
			else
			{
				adapter = (SectionedBaseAdapter) adapterView.getAdapter();
			}
			int section = adapter
					.getSectionForPosition(rawPosition - headCount);
			int position = adapter.getPositionInSectionForPosition(rawPosition
					- headCount);

			if (position == -1)
			{
				onSectionClick(adapterView, view, section, rawPosition, id);
			}
			else if (section == -2 || position == -2)
			{
				onHeadOrFooterClick(adapterView, view, rawPosition, id);
			}
			else
			{
				onItemClick(adapterView, view, section, position, rawPosition,
						id);
			}
		}

		public abstract void onHeadOrFooterClick(AdapterView<?> adapterView,
				View view, int position, long id);

		public abstract void onItemClick(AdapterView<?> adapterView, View view,
				int section, int position, int rawPosition, long id);

		public abstract void onSectionClick(AdapterView<?> adapterView,
				View view, int section, int rawPosition, long id);

	}
}
